import string
from datetime import datetime
from typing import Tuple

from mg_app_framework import get_logger

from common_bind_service.constant.code_generation_constant import CodeGenerationConstant
from common_bind_service.context_utils.context_utils import ContextUtils
from common_bind_service.dao.code_generation_dao import CodeGenerationDao, RuleCodeGenerationDao
from common_bind_service.models.code_generation import CodeGenerationRecord, RuleCodeGenerationRecord
from common_bind_service.models.param.code_param import CheckGenerateCoilNoParam, CheckGenerateCellSnParam, \
    CheckBindAndUnbindCodeParam
from common_bind_service.models.param.generate_code_rule_param import GenerateRuleCommonCode
from common_bind_service.models.serializer.response import Response


class CodeGenerationService:
    upper_case_letters: str = string.ascii_uppercase

    def __init__(self):
        self.generate_code_dao = CodeGenerationDao()
        self.rule_code_dao = RuleCodeGenerationDao()
        self.process_code_wip_type_map = {
            'C90': 1,
            'C100': 2,
        }

    async def generate_coil_no(self, params: CheckGenerateCoilNoParam):
        get_logger().info(f'generate_coil_no params: {params}')

        src_wip_no: list = params.src_wip_no
        if not src_wip_no or not src_wip_no[0]:
            return Response.fail('传入批次码为空')
        wip_count: int = int(params.wip_count)
        parent_code: str = src_wip_no[0].strip()  # 取第一个码
        exists = await self.generate_code_dao.get_latest_num_by_query({'parent_code': parent_code})
        last_no = int(exists.get('latest_num', 0)) if exists else 0
        child_code_list = []
        latest_num = 0
        for i in range(wip_count):
            latest_num = i + 1 + last_no
            child_code_list.append(parent_code + '-' + str(latest_num))
        generates = CodeGenerationRecord(src_wip_no=params.src_wip_no, wip_count=params.wip_count,
                                         parent_code=parent_code, child_codes=child_code_list,
                                         latest_num=latest_num,
                                         create_time=datetime.now())

        await self.generate_code_dao.insert_one(generates)
        ret_data = {'wip_no': child_code_list}
        return Response.success(ret_data)

    async def generate_cell_sn(self, params: CheckGenerateCellSnParam):
        get_logger().info(f'generate_single_code params: {params}')
        wip_type: int = params.wip_type
        wip_count: int = params.wip_count
        factory_code, line_code = params.factory_code, params.line_code
        null_error = []
        if not factory_code:
            null_error.append('工厂编码不能为空')
        if not line_code:
            null_error.append('产线编码不能为空')
        if null_error:
            return Response.fail('||'.join(null_error))
        factory, line = factory_code[0], line_code[0]
        common_code: GenerateRuleCommonCode = await self.get_common_component_code()
        equip_code = params.equip_code or ''
        if wip_type == 0:
            # 卷芯收尾胶码,卷绕工序生成IF030，入参wip_type=0   条码位数：12位
            dev_code_table_number_map = ContextUtils.get_dev_code_table_number_map()
            table_number: str = dev_code_table_number_map.get(equip_code, '')
            if not table_number:
                return Response.fail(f'设备{equip_code}机台号为空')
            table_number: int = int(table_number)
            if table_number in range(1, 10):
                table_number = str(table_number)
            elif table_number in range(10, 16):
                table_number = self.upper_case_letters[table_number - 10]
            else:
                return Response.fail(f'{equip_code}机台号错误，请到设备台账中维护')

            code_components = [factory, line, table_number, common_code.year,
                               common_code.month, common_code.day]
            code_prefix = ''.join(code_components)
        elif wip_type in [1, 2]:  # 此部分逻辑保留做测试用
            # 负极汇流盘焊接工序生成IF030，入参wip_type=1
            # 正极汇流盘焊接工序生成IF030，入参wip_type=2
            top_cover = 'A' if wip_type == 1 else 'C'
            code_components = [factory, line, top_cover, common_code.year,
                               common_code.month, common_code.day]
            code_prefix = ''.join(code_components)
        else:
            return Response.fail(f'请求的在制品条码类型错误:{wip_type}')
        now = datetime.now()
        td_zero_time = now.replace(hour=0, minute=0, second=0, microsecond=0)
        query = {'wip_type': wip_type, 'create_time': {'$gte': td_zero_time}}
        if wip_type == 0:
            query.update({'equip_code': equip_code})
        td_latest = await self.rule_code_dao.get_latest_serial_no_by_query(query)
        insert_datas = []  # 数据库插入数据
        cell_sn = []  # 返回码值列表
        last_no = int(td_latest.get('serial_no', 0)) if td_latest else 0
        for i in range(wip_count):
            new_serial_no = str(last_no + i + 1).zfill(6)
            code = code_prefix + new_serial_no
            cell_sn.append(code)
            generate = RuleCodeGenerationRecord(wip_type=wip_type, code=code, serial_no=new_serial_no,
                                                create_time=now, equip_code=equip_code)
            insert_datas.append(generate)
        await self.rule_code_dao.insert_many(insert_datas)
        ret_data = {'cell_sn': cell_sn}
        return Response.success(ret_data)

    async def generate_bind_code(self, params: CheckBindAndUnbindCodeParam):
        get_logger().info(f'generate_bind_code params: {params}')
        if params.process_code == 'C100':
            latest_type_one = await self.rule_code_dao.get_latest_data_by_query(
                {'wip_type': 1, 'bind_code': params.in_cell_sn})
            if latest_type_one:
                params.in_cell_sn = latest_type_one['code']
            else:
                return Response.fail(f'进站码{params.in_cell_sn}未绑定负极电芯码')
        in_cell_sn = params.in_cell_sn
        out_cell_sn = params.out_cell_sn
        check_code, in_cell_sn_exists, err = await self.bind_unbind_code_common_check(params)
        if err:
            return Response.fail(err)
        if check_code:
            if not check_code.bind_code:
                # 解绑情况下可重新绑定
                await self.rule_code_dao.update_by_id(check_code.id, {'bind_code': in_cell_sn})
            elif check_code.bind_code != in_cell_sn:
                return Response.fail(f'{out_cell_sn}已经绑定{check_code.bind_code}')
        else:
            if in_cell_sn_exists:
                return Response.fail(f'in_cell_sn={in_cell_sn}已经被code:{in_cell_sn_exists.code}绑定,不要重复绑定')
            now = datetime.now()
            wip_type = self.process_code_wip_type_map.get(params.process_code)
            insert_data = RuleCodeGenerationRecord(wip_type=wip_type, code=out_cell_sn, bind_code=in_cell_sn,
                                                   serial_no=out_cell_sn[-6:],
                                                   create_time=now, equip_code=params.equip_code)
            await self.rule_code_dao.insert_one(insert_data)
        return Response.success()

    async def unbind_code(self, params: CheckBindAndUnbindCodeParam):
        get_logger().info(f'unbind_code params: {params}')
        in_cell_sn = params.in_cell_sn
        out_cell_sn = params.out_cell_sn
        check_code, _, err = await self.bind_unbind_code_common_check(params)
        wip_type = self.process_code_wip_type_map.get(params.process_code)
        if err:
            return Response.fail(err)
        if not check_code:
            return Response.fail(f'process_code={params.process_code},wip_type={wip_type}下的code:{out_cell_sn}不存在')
        if not check_code.bind_code or check_code.bind_code != in_cell_sn:
            return Response.fail(f'条码{out_cell_sn}和{in_cell_sn}没有建立绑定关系，解绑失败')
        await self.rule_code_dao.update_by_id(check_code.id, {'bind_code': ''})
        return Response.success()

    async def bind_unbind_code_common_check(self, params: CheckBindAndUnbindCodeParam):
        in_cell_sn = params.in_cell_sn
        out_cell_sn = params.out_cell_sn
        process_code = params.process_code

        def multi_null_err_check():
            err_list = []
            null_err = 'in_cell_sn和out_cell_sn不能为空'
            process_code_error = '工序编码错误'
            if not in_cell_sn or not out_cell_sn:
                err_list.append(null_err)
            if not find_wip_type:
                err_list.append(process_code_error)
            return err_list

        find_wip_type = self.process_code_wip_type_map.get(process_code)
        errs = multi_null_err_check()
        if errs:
            return None, None, '||'.join(errs)
        query = {'wip_type': find_wip_type}
        check_code = await self.rule_code_dao.get_one_by_query({**query, **{'code': out_cell_sn}})
        in_cell_sn_exists = await self.rule_code_dao.get_one_by_query({**query, **{'bind_code': in_cell_sn}})
        return check_code, in_cell_sn_exists, ''

    @classmethod
    async def get_common_component_code(cls):
        upper_case_letters = cls.upper_case_letters
        now = datetime.now()
        product_year_code = upper_case_letters[now.year - 2021]  # 生产年份
        product_month_code = upper_case_letters[now.month - 1]  # 生产月份
        product_day_code = await cls.trans_digit_to_code(now.day)  # 生产日期
        return GenerateRuleCommonCode(year=product_year_code,
                                      month=product_month_code, day=product_day_code)

    @classmethod
    async def trans_digit_to_code(cls, src_no):
        src_no = int(src_no)
        if src_no in range(1, 6):
            code = str(src_no)
        elif src_no <= 31:
            code = cls.upper_case_letters[src_no - 6]
        else:
            code = ''
        return code
